package com.xyh.transaction.service.imp;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.xyh.transaction.common.*;
import com.xyh.transaction.entity.*;
import com.xyh.transaction.entity.dto.cart.CartDto;
import com.xyh.transaction.entity.dto.order.CreateOrderDto;
import com.xyh.transaction.entity.dto.order.SearchOrderDto;
import com.xyh.transaction.entity.dto.user.ArrDto;
import com.xyh.transaction.entity.vo.*;
import com.xyh.transaction.excel.ExportOrderVo;
import com.xyh.transaction.exception.BusinessException;
import com.xyh.transaction.listener.MapGoodsImageEvent;
import com.xyh.transaction.mapper.*;
import com.xyh.transaction.queue.OrderDelayQueue;
import com.xyh.transaction.service.OrderService;
import com.xyh.transaction.utils.ExcelUtil;
import com.xyh.transaction.utils.MappingUtil;
import com.xyh.transaction.utils.OrderNumberGenerator;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

@Slf4j
@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private CartMapper cartMapper;

    @Autowired
    private GoodsMapper goodsMapper;

    @Autowired
    private GoodsEvaluationMapper goodsEvaluationMapper;

    @Autowired
    private AddressMapper addressMapper;

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private OrderDelayQueue orderDelayQueue;

    @Autowired
    private ApplicationEventPublisher eventPublisher;

    // 共享的重复逻辑
    private void processOrderDetails(List<OrderDetail> orderDetails, List<GoodsVo> goodsVos,
                                     Summary summary, AtomicLong goodsCount,
                                     AtomicReference<BigDecimal> totalPrice) {
        for (OrderDetail orderDetail : orderDetails) {
            Long count = orderDetail.getCount();
            Integer goodsId = orderDetail.getGoodsId();

            Goods goods = goodsMapper.findById(goodsId);

            if (count > goods.getStock()){
                throw new BusinessException("商品库存不足，无法提交订单");
            }

            BigDecimal price = goods.getPrice();
            BigDecimal goodsTotalPrice = price.multiply(BigDecimal.valueOf(count));

            GoodsVo goodsVo = MappingUtil.mapObject(goods, GoodsVo.class);
            eventPublisher.publishEvent(new MapGoodsImageEvent(goods, goodsVo));
            goodsVo.setGoodsNum(count);
            goodsVo.setTotalPrice(goodsTotalPrice);
            goodsVo.setTotalPayPrice(goodsTotalPrice);
            goodsVos.add(goodsVo);

            totalPrice.getAndUpdate(total -> total.add(goodsTotalPrice));
            goodsCount.getAndAdd(count);
        }
    }

    @Override
    public OrderCheckoutInfo getCheckoutInfo(Integer orderId) {
        Integer userId = Current.getCurrentUser().getUser().getId();
        List<GoodsVo> goodsVos = new ArrayList<>();
        Summary summary = new Summary();
        AtomicLong goodsCount = new AtomicLong(0);
        AtomicReference<BigDecimal> totalPrice = new AtomicReference<>(BigDecimal.ZERO);
        BigDecimal postFee = new BigDecimal("0");

        List<OrderDetail> orderDetails;
        if (orderId != null) {
            orderDetails = orderDetailMapper.findByOrderId(orderId);
        } else {
            orderDetails = cartMapper.findByUserId(userId).stream()
                    .filter(cart -> cart.getSelected() == 1)
                    .map(cart -> {
                        OrderDetail orderDetail = new OrderDetail();
                        orderDetail.setGoodsId(cart.getGoodsId());
                        orderDetail.setCount(cart.getCount());
                        return orderDetail;
                    })
                    .collect(Collectors.toList());
        }
        processOrderDetails(orderDetails, goodsVos, summary, goodsCount, totalPrice);

        BigDecimal totalPayPrice = totalPrice.get().add(postFee);

        summary.setGoodsCount(goodsCount.get());
        summary.setTotalPrice(totalPrice.get());
        summary.setTotalPayPrice(totalPayPrice);
        summary.setPostFee(postFee);

        List<Address> addresses = addressMapper.findByUserId(userId);

        OrderCheckoutInfo checkoutInfo = new OrderCheckoutInfo();
        checkoutInfo.setGoods(goodsVos);
        checkoutInfo.setSummary(summary);
        checkoutInfo.setUserAddresses(addresses);

        return checkoutInfo;
    }

    @Transactional
    @Override
    public Integer createOrder(CreateOrderDto orderDto) {

        Integer userId = Current.getCurrentUser().getUser().getId();

        int addressId = orderDto.getAddressId();

        OrderDeliveryType orderDeliveryType = OrderDeliveryType.values()[orderDto.getDeliveryType()];
        OrderPayType orderPayType = OrderPayType.values()[orderDto.getPayType()];

        ArrayList<OrderDetail> orderDetails = new ArrayList<>();
        BigDecimal amount = new BigDecimal("0");
        if (orderDto.getGoods().isEmpty()) {
            throw new BusinessException("商品列表为空，无法提交订单");
        }
        Integer sellerId = null;
        ArrayList<Integer> selectedCartIds = new ArrayList<>();
        for (CartDto cart : orderDto.getGoods()) {
            Long count = cart.getCount();
            if (count <= 0){
                throw new BusinessException("商品数量小于等于0，无法提交订单");
            }
            selectedCartIds.add(cart.getId());
            OrderDetail orderDetail = new OrderDetail();
            orderDetail.setGoodsId(cart.getGoodsId());
            orderDetail.setCount(count);
            orderDetail.setCreateBy(userId);
            orderDetail.setUpdateBy(userId);
            orderDetail.setCreateTime(LocalDateTime.now());
            orderDetail.setUpdateTime(LocalDateTime.now());
            orderDetails.add(orderDetail);
            Goods goods = goodsMapper.findById(cart.getGoodsId());
            sellerId = goods.getCreateBy();
            amount = amount.add(goods.getPrice()
                    .multiply(BigDecimal.valueOf(count)));

            if (orderDetail.getCount() > goods.getStock()){
                throw new BusinessException("商品库存不足，无法创建订单");
            }

            // 减去商品库存
            Goods updateGoods = new Goods();
            updateGoods.setId(orderDetail.getGoodsId());
            updateGoods.setStock(goods.getStock() - orderDetail.getCount().intValue());
            goodsMapper.update(updateGoods);
        }

        String orderNo = OrderNumberGenerator.generateOrderNumber();

        Order order = new Order();
        order.setStatus(OrderStatus.WAIT_PAY);
        order.setDeliveryType(orderDeliveryType);
        order.setPayType(orderPayType);
        order.setAmount(amount);
        order.setOrderNo(orderNo);
        order.setAddressId(addressId);
        order.setCreateBy(userId);
        order.setUpdateBy(userId);
        order.setCreateTime(LocalDateTime.now());
        order.setUpdateTime(LocalDateTime.now());
        order.setSellerId(sellerId);// 卖家

        orderMapper.inset(order);
        Integer orderId = order.getId();
        // 存入order_delay_queue，设置倒计时30分钟
        orderDelayQueue.addToDelayQueue(order,30L * 60L * 1000L);
        // 清除购物车中已创建订单的商品
        cartMapper.deleteBatchById(selectedCartIds);
        // 保存订单详单
        for (OrderDetail orderDetail : orderDetails) {
            orderDetail.setOrderId(orderId);
            orderDetailMapper.insert(orderDetail);
        }
        return orderId;
    }

    @Override
    public PayInfo getPayInfo(Integer orderId) {
        Order order = orderMapper.findById(orderId);

        Long expiredTime = orderDelayQueue
                .getExpiredTime(order.getId() + "_" + order.getOrderNo());

        Long expire = expiredTime
                - System.currentTimeMillis();

        PayInfo payInfo = new PayInfo();
        payInfo.setPayMoney(order.getAmount());
        payInfo.setCountdown(expire);

        return payInfo;
    }

    @Override
    public PageInfo<OrderVo> getUserOrderList(SearchOrderDto orderDto) {

        Integer pageNum = orderDto.getPageNum();
        Integer pageSize = orderDto.getPageSize();

        if (pageNum != null && pageSize != null) {
            PageHelper.startPage(pageNum, pageSize);
        }

        Order order = new Order();
        if (orderDto.getStatus() != null) {
            order.setStatus(OrderStatus.values()[orderDto.getStatus()]);
        }

        // 非管理员查看与自己相关的订单
        if (!Current.getCurrentUser().getRoles().stream().map(Role::getId).collect(Collectors.toList())
                .contains(UserRole.Admin.getCode())){
            order.setCreateBy(Current.getCurrentUser().getUser().getId());
        }

        List<Order> orderList = orderMapper.findAll(order);

        PageInfo pageInfo = new PageInfo(orderList);

        List<OrderVo> orderVoList = orderList.stream()
                .sorted(Comparator.comparing(Order::getCreateTime).reversed())// 降序排序
                .map(this::mappingOrderVo)
                .collect(Collectors.toList());

        pageInfo.setList(orderVoList);

        return pageInfo;
    }

    @NotNull
    private OrderVo mappingOrderVo(Order order) {
        OrderStatus status = order.getStatus();
        OrderVo orderVo = new OrderVo();
        orderVo.setId(order.getId());
        orderVo.setOrderNo(order.getOrderNo());
        orderVo.setAmount(order.getAmount());
        orderVo.setStatus(status.ordinal());
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(Constant.DATE_FORMAT_DEFAULT);
        orderVo.setCreateTime(order.getCreateTime().format(formatter));
        orderVo.setUpdateTime(order.getUpdateTime().format(formatter));
        if (order.getPayTime() != null) {
            orderVo.setPayTime(order.getPayTime().format(formatter));
        }
        if (order.getDeliveryTime() != null) {
            orderVo.setDeliveryTime(order.getDeliveryTime().format(formatter));
        }
        if (order.getConfirmTime() != null) {
            orderVo.setConfirmTime(order.getConfirmTime().format(formatter));
        }
        if (order.getCompleteTime() != null) {
            orderVo.setCompleteTime(order.getCompleteTime().format(formatter));
        }
        orderVo.setPostFee(new BigDecimal("0"));
        if (status.equals(OrderStatus.WAIT_PAY)) {
            orderVo.setCountdown(orderDelayQueue.getExpiredTime(order.getId() + "_" + order.getOrderNo())
                    - System.currentTimeMillis());
        }
        ArrayList<GoodsVo> goodsVos = new ArrayList<>();
        Long totalCount = 0L;
        for (OrderDetail orderDetail : orderDetailMapper.findByOrderId(order.getId())) {
            Goods goods = goodsMapper.findById(orderDetail.getGoodsId());
            GoodsVo goodsVo = MappingUtil.mapObject(goods, GoodsVo.class);
            eventPublisher.publishEvent(new MapGoodsImageEvent(goods, goodsVo));
            goodsVo.setGoodsNum(orderDetail.getCount());
            if (status.equals(OrderStatus.COMPLETE)) {
                // 该订单下的商品评价
                GoodsEvaluation goodsEvaluation = goodsEvaluationMapper.
                        findByOrderIdAndGoodsId(orderDetail.getOrderId(), orderDetail.getGoodsId());
                ArrayList<GoodsEvaluation> evaluations = new ArrayList<>();
                evaluations.add(goodsEvaluation);
                goodsVo.setEvaluations(evaluations);
            }
            totalCount += orderDetail.getCount();
            goodsVos.add(goodsVo);
        }
        orderVo.setGoods(goodsVos);
        orderVo.setTotalCount(totalCount);
        // 地址信息
        Address address = addressMapper.findById(order.getAddressId());
        orderVo.setReceiver(address.getReceiver());
        orderVo.setContract(address.getContact());
        orderVo.setAddress(address.getFullLocation() + " " + address.getAddress());
        // 支付方式
        orderVo.setPayType(order.getPayType().getDetail());
        // 配送方式
        orderVo.setDeliveryType(order.getDeliveryType().getDetail());
        // 买家
        orderVo.setBuyer(userMapper.findById(order.getCreateBy()).getUsername());

        return orderVo;
    }

    @Override
    public void updateStatus(SearchOrderDto orderDto) {
        Integer id = orderDto.getId();
        Integer status = orderDto.getStatus();

        Order order = new Order();
        mappingStatusTime(OrderStatus.values()[status], order);
        if (OrderStatus.values()[status].equals(OrderStatus.CANCELLED)) {
            for (OrderDetail orderDetail : orderDetailMapper.findByOrderId(id)) {
                // 恢复库存
                Goods updateGoods = goodsMapper.findById(orderDetail.getGoodsId());
                Goods goods = new Goods();
                goods.setId(orderDetail.getGoodsId());
                goods.setStock(updateGoods.getStock() + orderDetail.getCount().intValue());
                goodsMapper.update(updateGoods);
            }
        }
        order.setId(id);
        order.setStatus(OrderStatus.values()[status]);
        order.setUpdateBy(Current.getCurrentUser().getUser().getId());
        order.setUpdateTime(LocalDateTime.now());
        orderMapper.updateById(order);
    }

    private void mappingStatusTime(OrderStatus status, Order order) {
        switch (status) {
            case WAIT_RECEIPT:
                order.setDeliveryTime(LocalDateTime.now());
                break;
            case WAIT_EVALUATE:
                order.setConfirmTime(LocalDateTime.now());
                break;
            case COMPLETE:
                order.setEvaluateTime(LocalDateTime.now());
                order.setCompleteTime(LocalDateTime.now());
                break;
            default:
                log.error("错误的订单状态！");
                throw new BusinessException("错误的订单状态！");
        }
    }

    @Override
    public OrderVo getOrderInfo(String id) {
        Order order = orderMapper.findById(Integer.parseInt(id));
        return mappingOrderVo(order);
    }

    @Override
    public PageInfo<OrderVo> getAminOrderList(SearchOrderDto orderDto) {
        Integer pageNum = orderDto.getPageNum();
        Integer pageSize = orderDto.getPageSize();

        if (pageNum != null && pageSize != null) {
            PageHelper.startPage(pageNum, pageSize);
        }

        Order order = MappingUtil.mapObject(orderDto, Order.class);
        if (!Current.getCurrentUser().getRoles().stream().map(Role::getId).collect(Collectors.toList())
                .contains(UserRole.Admin.getCode())) {
            order.setSellerId(Current.getCurrentUser().getUser().getId());
        }
        if (orderDto.getStatus() != null){
            order.setStatus(OrderStatus.values()[orderDto.getStatus()]);
        }
        if (StringUtils.isNotBlank(orderDto.getDeliveryType())) {
            order.setDeliveryType(OrderDeliveryType.getByDetail(orderDto.getDeliveryType()));
        }
        if (StringUtils.isNotBlank(orderDto.getPayType())) {
            order.setPayType(OrderPayType.getByDetail(orderDto.getPayType()));
        }

        List<Order> orderList = orderMapper.findAll(order);
        PageInfo pageInfo = new PageInfo(orderList);

        List<OrderVo> orderVoList = orderList.stream()
                .sorted(Comparator.comparing(Order::getCreateTime).reversed()) // 倒叙排序
                .map(this::mappingOrderVo)
                .collect(Collectors.toList());
        pageInfo.setList(orderVoList);

        return pageInfo;
    }

    @Override
    public List<OrderStatusVo> getOrderStatusList() {
        return Arrays.stream(OrderStatus.values()).map(orderStatus -> {
            OrderStatusVo orderStatusVo = new OrderStatusVo();
            orderStatusVo.setLabel(orderStatus.getDetail());
            orderStatusVo.setValue(orderStatus.ordinal());
            return orderStatusVo;
        }).collect(Collectors.toList());
    }

    @Override
    public List<LabelValue> getPayTypeList() {
        return Arrays.stream(OrderPayType.values()).map(orderPayType -> {
            LabelValue labelValue = new LabelValue();
            labelValue.setLabel(orderPayType.getDetail());
            labelValue.setValue(orderPayType.getDetail());
            return labelValue;
        }).collect(Collectors.toList());
    }

    @Override
    public List<LabelValue> getDeliveryTypeList() {
        return Arrays.stream(OrderDeliveryType.values()).map(orderDeliveryType -> {
            LabelValue labelValue = new LabelValue();
            labelValue.setLabel(orderDeliveryType.getDetail());
            labelValue.setValue(orderDeliveryType.getDetail());
            return labelValue;
        }).collect(Collectors.toList());
    }

    @Override
    public void deliveryOrder(ArrDto dto) {
        for (Integer id : dto.getId()) {
            Order order = new Order();
            mappingStatusTime(OrderStatus.WAIT_RECEIPT, order);
            order.setId(id);
            order.setStatus(OrderStatus.WAIT_RECEIPT);
            order.setUpdateBy(Current.getCurrentUser().getUser().getId());
            order.setUpdateTime(LocalDateTime.now());
            orderMapper.updateById(order);
        }
    }

    @Override
    public ResponseEntity<byte[]> exportOrderList(SearchOrderDto orderDto) {
        Order order = MappingUtil.mapObject(orderDto, Order.class);
        if (!Current.getCurrentUser().getRoles().stream().map(Role::getId).collect(Collectors.toList())
                .contains(UserRole.Admin.getCode())) {
            order.setSellerId(Current.getCurrentUser().getUser().getId());
        }
        if (orderDto.getStatus() != null){
            order.setStatus(OrderStatus.values()[orderDto.getStatus()]);
        }
        if (StringUtils.isNotBlank(orderDto.getDeliveryType())){
            order.setDeliveryType(OrderDeliveryType.getByDetail(orderDto.getDeliveryType()));
        }
        if (StringUtils.isNotBlank(orderDto.getPayType())){
            order.setPayType(OrderPayType.getByDetail(orderDto.getPayType()));
        }

        List<ExportOrderVo> exportOrderVoList = orderMapper.findAll(order).stream().map(order1 -> {
            ExportOrderVo exportOrderVo = new ExportOrderVo();
            exportOrderVo.setAmount(order1.getAmount());
            exportOrderVo.setOrderNo(order1.getOrderNo());
            exportOrderVo.setDeliveryType(order1.getDeliveryType().getDetail());
            exportOrderVo.setPayType(order1.getPayType().getDetail());
            exportOrderVo.setStatus(order1.getStatus().getDetail());
            exportOrderVo.setBuyer(userMapper.findById(order1.getSellerId()).getUsername());
            exportOrderVo.setCreateTime(order1.getCreateTime().format(DateTimeFormatter.ofPattern(Constant.DATE_FORMAT_DEFAULT)));
            Address address = addressMapper.findById(order1.getAddressId());
            exportOrderVo.setReceiver(address.getReceiver());
            exportOrderVo.setAddress(address.getFullLocation() + " " + address.getAddress());
            return exportOrderVo;
        }).collect(Collectors.toList());

        return ExcelUtil.exportToExcel(exportOrderVoList,"orderList.xlsx", ExportOrderVo.class);
    }
}
